package com.zyw.auth.service.impl;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zyw.auth.common.IConstants;
import com.zyw.auth.common.PasswordHelper;
import com.zyw.auth.common.ShiroAuthHelper;
import com.zyw.auth.dao.AdminRoleDao;
import com.zyw.auth.dao.AdministratorDao;
import com.zyw.auth.dao.LoginLogoutLogDao;
import com.zyw.auth.dao.RightsDao;
import com.zyw.auth.dao.UserOperationLogDao;
import com.zyw.auth.domain.AdminRole;
import com.zyw.auth.domain.Administrator;
import com.zyw.auth.domain.LogLoginLogout;
import com.zyw.auth.domain.LogUserOperation;
import com.zyw.auth.domain.Rights;
import com.zyw.auth.model.AdministratorEditModel;
import com.zyw.auth.model.AdministratorListItem;
import com.zyw.auth.model.AdministratorQueryParams;
import com.zyw.auth.model.RightsVo;
import com.zyw.auth.service.AdministratorService;
import com.zyw.common.RespCode;
import com.zyw.common.RespResult;
import com.zyw.common.util.DateUtil;
import com.zyw.common.util.JsonUtil;
import com.zyw.kayla.infra.cache.common.page.Page;
import com.zyw.kayla.infra.cache.common.page.PageHelper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service("administratorService")
public class AdministratorServiceImpl implements AdministratorService {

    private static final Logger logger = LoggerFactory.getLogger(AdministratorServiceImpl.class);

    @Autowired
    private AdministratorDao adminDao;
    @Autowired
    private AdminRoleDao adminRoleDao;
    @Autowired
    private RightsDao rightsDao;
    @Autowired
    private LoginLogoutLogDao loginLogoutLogDao;
    @Autowired
    private UserOperationLogDao LogUserOperationDao;

    /**
     * 用户登录
     **/
    @Transactional
    @Override
    public RespResult<Object> login(Administrator admin, String clientAgent) {
        RespResult<Object> result = new RespResult<>();
        Subject currentUser = SecurityUtils.getSubject();
        Administrator currentAdmin;
        // 校验用户身份
        if (!currentUser.isAuthenticated()) {
            // 用户身份未验证（未登录）
            result = doAuthenticate(currentUser, admin.getUserName().trim(), admin.getPassword());
        } else {
            // 已认证，不同帐号切换登录
            currentAdmin = ShiroAuthHelper.getCurrentAdmin();
            if (!currentAdmin.getUserName().equals(admin.getUserName().trim())) {
                // 重新证认
                result = doAuthenticate(currentUser, admin.getUserName().trim(), admin.getPassword());
            } else {
                // 不再认证
                result.addSuccess(RespCode.OK);
            }
        }
        // 日志
        LogLoginLogout logBean = new LogLoginLogout();
        logBean.setOperationType(IConstants.LOG_USER_LOGIN);
        logBean.setUserName(admin.getUserName());
        logBean.setClientIp(currentUser.getSession().getHost());
        logBean.setUserAgent(clientAgent);
        logBean.setCreateTime(DateUtil.now());

        if (result.hasErrors()) {
            logBean.setResult("登录失败[" + result.getMessage() + "]");
            loginLogoutLogDao.insertSelective(logBean);
            return result;
        } else {
            currentAdmin = ShiroAuthHelper.getCurrentAdmin();
            Administrator updateBean = new Administrator();
            updateBean.setAdminId(currentAdmin.getAdminId());
            updateBean.setLastLoginIp(currentUser.getSession().getHost());
            updateBean.setUpdateTime(DateUtil.now());
            updateBean.setLastLoginTime(DateUtil.now());
            adminDao.updateByPrimaryKeySelective(updateBean);

            logBean.setResult("登录成功");
            loginLogoutLogDao.insertSelective(logBean);
        }
        AdministratorListItem adminVO = new AdministratorListItem();
        BeanUtils.copyProperties(currentAdmin, adminVO, "password");
        return result.addSuccess(adminVO);
    }

    // 调用shiro身份验证
    private RespResult<Object> doAuthenticate(Subject currentUser, String userName, String password) {
        RespResult<Object> result = new RespResult<>();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        try {
            currentUser.login(token);
        } catch (UnknownAccountException | IncorrectCredentialsException ex) {
            return result.addError(RespCode.ERR_INCORRECT_ACCOUNT);
        } catch (LockedAccountException ex) {
            return result.addError(RespCode.ERR_LOCKED_ACCOUNT);
        } catch (AuthenticationException ex) {
            return result.addError(RespCode.ERR_4011.getId(), "登录失败：" + ex.getMessage());
        }
        return result.addSuccess(RespCode.OK);
    }

    /**
     * 用户登出
     **/
    @Transactional
    @Override
    public RespResult<String> logout(String clientAgent) {
        Subject subject = SecurityUtils.getSubject();
        Administrator admin = ShiroAuthHelper.getCurrentAdmin();
        subject.logout();
        //记录日志
        LogLoginLogout logBean = new LogLoginLogout();
        logBean.setOperationType(IConstants.lOG_USER_LOGOUT);
        logBean.setUserName(admin.getUserName());

        logBean.setClientIp(subject.getSession().getHost());
        logBean.setUserAgent(clientAgent);
        logBean.setCreateTime(DateUtil.now());
        logBean.setResult("登出成功");
        loginLogoutLogDao.insertSelective(logBean);
        return new RespResult<>(RespCode.OK);
    }

    /**
     * <p>查询初始菜单（一级菜单 + 【第一个】一级菜单下子菜单</p>
     * <p>根据当前用户登录，获取初始菜单列表</p>
     */
    @Transactional(readOnly = true)
    @Override
    public Map<String, Object> queryMainMenus() {
        Map<String, Object> mainMenus = Maps.newHashMap();
        Long adminId = ShiroAuthHelper.getCurrentAdminId();
        List<Rights> topMenus = rightsDao.queryTopMenus(adminId, IConstants.C_PROPERTY_VALID);
        List<RightsVo> sideMenus = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(topMenus)) {
            sideMenus = querySideMenus(topMenus.get(0).getRightsId());
        }
        mainMenus.put("topMenus", topMenus);
        mainMenus.put("sideMenus", sideMenus);
        return mainMenus;
    }

    /**
     * <p>查询二级菜单（包括页面操作权限）</p>
     * <p>根据当前用户登录，获取二级菜单列表</p>
     */
    @Transactional(readOnly = true)
    @Override
    public List<RightsVo> querySideMenus(Long menuParentId) {
        List<RightsVo> sideMenus = Lists.newArrayList();
        Long adminId = ShiroAuthHelper.getCurrentAdminId();
        List<Rights> allRights = rightsDao.querySideRights(adminId, IConstants.C_PROPERTY_VALID, menuParentId);
        if (!CollectionUtils.isEmpty(allRights)) {
            Map<Long, RightsVo> root = Maps.newHashMap();
            List<RightsVo> missList = Lists.newArrayList();
            for (Rights rights : allRights) {
                RightsVo vo = new RightsVo();
                BeanUtils.copyProperties(rights, vo);
                root.put(vo.getRightsId(), vo);
                // 直属菜单
                if (menuParentId.compareTo(vo.getParentId()) == 0) {
                    sideMenus.add(vo);
                } else {
                    RightsVo vparent = root.get(vo.getParentId());
                    if (vparent != null) {
                        vparent.getChildren().add(vo);
                    } else {
                        missList.add(vo);
                    }
                }
            }
            // 增补挂靠父节点
            if (!missList.isEmpty()) {
                for (RightsVo vo : missList) {
                    RightsVo vparent = root.get(vo.getParentId());
                    if (vparent != null) {
                        vparent.getChildren().add(vo);
                    }
                }
            }
            root.clear();
            missList.clear();
        }

        return sideMenus;
    }

    /**
     * 修改密码(用户本人操作)
     **/
    @Transactional
    @Override
    public RespResult<String> changePassword(Administrator admin, String oldPassword, String newPassword, String confirmPassword) {
        RespResult<String> result = new RespResult<>();
        String oldPasswordMD5 = PasswordHelper.encryptPasswordMD5(oldPassword, admin.getPasswordSalt());
        if (!oldPasswordMD5.equals(admin.getPassword())) {
            logger.error("密码修改失败，旧密码错误[name=" + admin.getUserName() + "]");
            return result.addError("旧密码错误");
        }
        String newPasswordMD5 = PasswordHelper.encryptPasswordMD5(newPassword, admin.getPasswordSalt());
        Administrator updateBean = new Administrator();
        updateBean.setAdminId(admin.getAdminId());
        updateBean.setPassword(newPasswordMD5);
        updateBean.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        updateBean.setUpdateTime(DateUtil.now());
        adminDao.updateByPrimaryKeySelective(updateBean);
        //记录操作日志
        String beforeJson = JsonUtil.objectToJson(admin);
        admin.setPassword(newPasswordMD5);
        admin.setUpdateTime(DateUtil.now());
        String afterJson = JsonUtil.objectToJson(admin);
        LogUserOperation logBean = new LogUserOperation();
        logBean.setAfterData(beforeJson);
        logBean.setBeforeData(afterJson);
        logBean.setDataId(String.valueOf(admin.getAdminId()));
        logBean.setOperatorId(admin.getAdminId());
        logBean.setCreateTime(DateUtil.now());
        logBean.setAction(IConstants.LOG_USER_OPER_CHG_PASSWD);
        LogUserOperationDao.insert(logBean);
        return result.addSuccess(IConstants.RESULT_UPDATE_SUCCESS);
    }

    /**
     * 重置密码（管理员操作）
     *
     * @return
     */
    @Transactional
    @Override
    public RespResult<String> resetPassword(Long adminId, String resetPassword) {
        RespResult<String> result = new RespResult<>();
        Administrator admin = adminDao.selectByPrimaryKey(adminId);
        if (admin == null) {
            return result.addError("管理员id[" + adminId + "]不存在");
        }
        String passwordMD5 = PasswordHelper.encryptPasswordMD5(resetPassword, admin.getPasswordSalt());
        Administrator updateBean = new Administrator();
        updateBean.setAdminId(admin.getAdminId());
        updateBean.setPassword(passwordMD5);
        updateBean.setUpdateTime(DateUtil.now());
        updateBean.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        adminDao.updateByPrimaryKeySelective(updateBean);
        //记录日志
        String beforeJson = JsonUtil.objectToJson(admin);
        admin.setPassword(passwordMD5);
        admin.setUpdateTime(DateUtil.now());
        admin.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        String afterJson = JsonUtil.objectToJson(admin);
        LogUserOperation logBean = new LogUserOperation();
        logBean.setDataId(String.valueOf(admin.getAdminId()));
        logBean.setCreateTime(DateUtil.now());
        logBean.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        logBean.setAction(IConstants.LOG_USER_OPER_REST_PASSWD);
        logBean.setBeforeData(beforeJson);
        logBean.setAfterData(afterJson);
        LogUserOperationDao.insertSelective(logBean);
        return result.addSuccess("重置密码成功");
    }

    /**
     * 创建管理员
     *
     * @return
     */
    @Override
    @Transactional
    public RespResult<String> createAdministrator(AdministratorEditModel adminEditModel) {
        RespResult<String> result = new RespResult<>();
        Administrator administrator = adminDao.findByUserName(adminEditModel.getUserName().trim(), null);
        if (null != administrator) {
            return result.addError("用户名已经被使用");
        }
        String salt = PasswordHelper.generateCredentialsSalt();
        Administrator admin = new Administrator();
        admin.setPassword(PasswordHelper.encryptPasswordMD5(adminEditModel.getPassword(), salt));
        admin.setPasswordSalt(salt);
        Long currentAdminId = ShiroAuthHelper.getCurrentAdminId();
        admin.setCreatorId(currentAdminId);
        admin.setCreateTime(DateUtil.now());
        admin.setProperty(IConstants.C_PROPERTY_VALID);
        admin.setRealName(adminEditModel.getRealName());
        admin.setUserName(adminEditModel.getUserName().trim());
        admin.setUserStatus(adminEditModel.getUserStatus());
        adminDao.insertSelective(admin);
        AdminRole adminRole;
        List<AdminRole> adminRoles = new ArrayList<>();
        for (Long roleId : adminEditModel.getRoleIds()) {
            adminRole = new AdminRole();
            adminRole.setRoleId(roleId);
            adminRole.setAdminId(admin.getAdminId());
            adminRole.setCreatorId(currentAdminId);
            adminRole.setCreateTime(DateUtil.now());
            adminRoles.add(adminRole);
        }
        adminRoleDao.insertListCustomized(adminRoles);
        return result.addSuccess(IConstants.RESULT_ADD_SUCCESS);
    }

    /**
     * 分页查询管理员列表
     *
     * @return
     */
    @Override
    @Transactional
    public Page<AdministratorListItem> queryPage(AdministratorQueryParams adminQueryParams) {
        PageHelper<AdministratorQueryParams> pageHelper = new PageHelper<>(adminQueryParams);
        List<AdministratorListItem> list = adminDao.queryPageData(pageHelper.getRequestParameterMap());
        long totalCount = adminDao.getTotalCount(adminQueryParams);
        Page<AdministratorListItem> page = pageHelper.createPage(list, totalCount);
        return page;
    }

    /**
     * 更新管理员
     *
     * @return
     */
    @Override
    @Transactional
    public RespResult<String> updateAdministrator(Long adminId, AdministratorEditModel adminEditModel) {
        RespResult<String> result = new RespResult<>();
        if (adminId.longValue() < IConstants.SUPER_ADMIN_ID_THRESHOLD.intValue()) {
            return result.addError("系统预设管理员，禁止修改");
        }
        Administrator targetAdmin = adminDao.selectByPrimaryKey(adminId);
        if ((null == targetAdmin) || (targetAdmin.getProperty().compareTo(IConstants.C_PROPERTY_INVALID) == 0)) {
            return result.addError("管理员不存在");
        }
        Administrator updateBean = new Administrator();
        updateBean.setAdminId(targetAdmin.getAdminId());
        updateBean.setRealName(adminEditModel.getRealName());
        updateBean.setUserStatus(adminEditModel.getUserStatus());
        updateBean.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        updateBean.setUpdateTime(DateUtil.now());
        //修改管理员表
        adminDao.updateByPrimaryKeySelective(updateBean);
        //修改管理员-角色关系表
        AdminRole adminRole;
        List<AdminRole> adminRoles = new ArrayList<>();
        List<Long> beforeRoleIds = null;
        if (null != adminEditModel.getRoleIds() && !adminEditModel.getRoleIds().isEmpty()) {
            for (Long roleId : adminEditModel.getRoleIds()) {
                adminRole = new AdminRole();
                adminRole.setRoleId(roleId);
                adminRole.setAdminId(targetAdmin.getAdminId());
                adminRole.setCreatorId(ShiroAuthHelper.getCurrentAdminId());
                adminRole.setCreateTime(DateUtil.now());
                adminRoles.add(adminRole);
            }
            beforeRoleIds = adminRoleDao.updateAdminRoles(adminRoles);
        }
        //记录操作日志
        String beforeJson = JsonUtil.objectToJson(targetAdmin);
        targetAdmin.setRealName(adminEditModel.getRealName());
        targetAdmin.setUserStatus(adminEditModel.getUserStatus());
        String afterJson = JsonUtil.objectToJson(targetAdmin);
        LogUserOperation logBean = new LogUserOperation();
        logBean.setDataId(String.valueOf(targetAdmin.getAdminId()));
        logBean.setCreateTime(DateUtil.now());
        logBean.setOperatorId(ShiroAuthHelper.getCurrentAdminId());
        logBean.setAction(IConstants.LOG_USER_OPER_CHG_ADMIN_INFO);
        logBean.setBeforeData(beforeJson);
        logBean.setAfterData(afterJson);
        LogUserOperationDao.insertSelective(logBean);
        if (!CollectionUtils.isEmpty(beforeRoleIds)) {
            Map<String, Object> jsonMap = Maps.newHashMap();
            jsonMap.put("adminId", targetAdmin.getAdminId());
            jsonMap.put("roleIds", beforeRoleIds);
            logBean.setBeforeData(JsonUtil.objectToJson(jsonMap));
            jsonMap.put("roleIds", adminEditModel.getRoleIds());
            logBean.setAfterData(JsonUtil.objectToJson(jsonMap));
            logBean.setActSeq("1");
            LogUserOperationDao.insertSelective(logBean);
        }
        return result.addSuccess(IConstants.RESULT_UPDATE_SUCCESS);
    }

    /**
     * 删除管理员
     *
     * @param adminId
     * @return
     */
    @Override
    @Transactional
    public RespResult<String> deleteAdministrator(Long adminId) {
        RespResult<String> result = new RespResult<>();
        if (adminId < IConstants.SUPER_ADMIN_ID_THRESHOLD) {
            return result.addError("系统预设管理员，禁止删除");
        }
        Long currentAdminId = ShiroAuthHelper.getCurrentAdminId();
        if (null != currentAdminId && (0 == currentAdminId.compareTo(adminId))) {
            return result.addError("不能删除当前登录账号");
        }
        Administrator targetAdmin = adminDao.selectByPrimaryKey(adminId);
        if (null == targetAdmin || (IConstants.C_PROPERTY_INVALID.compareTo(targetAdmin.getProperty()) == 0)) {
            return result.addError("管理员不存在");
        }
        Administrator updateBean = new Administrator();
        updateBean.setAdminId(adminId);
        updateBean.setProperty(IConstants.C_PROPERTY_INVALID);
        updateBean.setOperatorId(currentAdminId);
        updateBean.setUpdateTime(DateUtil.now());
        adminDao.updateByPrimaryKeySelective(updateBean);
        //记录操作日志
        String beforeJson = JsonUtil.objectToJson(targetAdmin);
        targetAdmin.setProperty(IConstants.C_PROPERTY_INVALID);
        targetAdmin.setUpdateTime(DateUtil.now());
        String afterJson = JsonUtil.objectToJson(targetAdmin);
        LogUserOperation logBean = new LogUserOperation();
        logBean.setDataId(String.valueOf(adminId));
        logBean.setCreateTime(DateUtil.now());
        logBean.setOperatorId(currentAdminId);
        logBean.setAction(IConstants.LOG_USER_OPER_DELETE_ADMIN);
        logBean.setBeforeData(beforeJson);
        logBean.setAfterData(afterJson);
        LogUserOperationDao.insertSelective(logBean);
        return result.addSuccess(IConstants.RESULT_DELETE_SUCCESS);
    }

    @Override
    @Transactional
    public AdministratorEditModel findEditModel(Long adminId) {
        return adminDao.findEditModel(adminId);
    }

    /**
     * 检查验证码uuid信息
     *
     * @return
     */
    @Override
    public boolean checkCaptcha(String uuidParam, String captchaCodeParam) {
        Session session = ShiroAuthHelper.getCurrentSession();
        if (session == null) {
            return false;
        }
        String uuid = (String) session.getAttribute(IConstants.LOGIN_UUID_SESSION_KEY);
        String captchaCode = (String) session.getAttribute(IConstants.LOGIN_CAPTCHA_SESSION_KEY);
        if (uuid == null || !uuidParam.equals(uuid)) {
            return false;
        }
        if (!captchaCodeParam.toLowerCase().equals(captchaCode.toLowerCase())) {
            return false;
        }
        return true;
    }
    
    /**
     * 更新管理员-用户状态
     * @param adminId 管理员ID
     * @param userStatus 用户状态
     * @return 返回操作结果
     */
    @Override
    @Transactional
    public RespResult<String> updateAdminStatus(Long adminId, Integer userStatus) {
    	RespResult<String> result = new RespResult<>();
        if (adminId.longValue() < IConstants.SUPER_ADMIN_ID_THRESHOLD.intValue()) {
            return result.addError("系统预设管理员，禁止删除");
        }
        Long currentAdminId = ShiroAuthHelper.getCurrentAdminId();
        if (null != currentAdminId && (0 == currentAdminId.compareTo(adminId))) {
            return result.addError("不能停用当前登录帐号");
        }
        Administrator targetAdmin = adminDao.selectByPrimaryKey(adminId);
        if (null == targetAdmin || (IConstants.C_PROPERTY_INVALID.compareTo(targetAdmin.getProperty()) == 0)) {
            return result.addError("管理员不存在");
        }
        Administrator updateBean = new Administrator();
        updateBean.setAdminId(adminId);
        updateBean.setUserStatus(userStatus);
        updateBean.setOperatorId(currentAdminId);
        updateBean.setUpdateTime(DateUtil.now());
        adminDao.updateByPrimaryKeySelective(updateBean);
        //记录操作日志
        String beforeJson = JsonUtil.objectToJson(targetAdmin);
        String afterJson = JsonUtil.objectToJson(updateBean);
        LogUserOperation logBean = new LogUserOperation();
        logBean.setDataId(String.valueOf(adminId));
        logBean.setCreateTime(DateUtil.now());
        logBean.setOperatorId(currentAdminId);
        logBean.setAction(IConstants.LOG_USER_OPER_FORBIDDEN_ADMIN);
        logBean.setBeforeData(beforeJson);
        logBean.setAfterData(afterJson);
        LogUserOperationDao.insertSelective(logBean);
        return result.addSuccess("操作成功");
    }
    
    /**
     * 根据条件查询记录集
     * @param adminQueryParams 查询条件
     * @return 返回集合列表
     */
    @Override
    @Transactional
	public List<AdministratorListItem> queryAdminList(AdministratorQueryParams adminQueryParams) {
    	adminQueryParams.setPageNumber(1);
    	adminQueryParams.setPageSize(IConstants.EXPORT_MAX_SZIE);
		PageHelper<AdministratorQueryParams> pageHelper = new PageHelper<>(adminQueryParams);
		return adminDao.queryPageData(pageHelper.getRequestParameterMap());
	}

}
